package com.gitee.wsl.compose.ext.math

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import com.gitee.wsl.math.Math
import kotlin.math.atan
import kotlin.math.cos
import kotlin.math.max
import kotlin.math.min


/**
 * The angle of the vector such that:
 *
 * - 12 o'clock = 0 degrees
 * - 3 o'clock = 90 degrees
 */
@Suppress("MagicNumber")
fun Math.angleDegrees(vector: Offset): Float  {
    val (x, y) = vector
    val deg = when {
        x == 0f -> when {
            y > 0 -> 90.0
            y < 0 -> 270.0
            else -> 0.0
        }
        y == 0f -> when {
            x > 0 -> 0.0
            x < 0 -> 180.0
            else -> 0.0
        }
        else -> {
            val deg = atan(y / x) * 180.0 / PI
            when {
                x < 0 -> 180 + deg
                y < 0 -> 360 + deg
                else -> deg
            }
        }
    }

    return (deg.toFloat() + 90) % 360
}

@Suppress("MagicNumber")
fun Math.scalarComponentOf(v1: Offset, v2: Offset): Float {
    val theta = angleDegrees(v1) - angleDegrees(v2)
    val l = v1.getDistance()

    return (l * cos(theta / 180 * PI)).toFloat()
}

fun Math.proportionOf(v1: Offset, v2: Offset): Float {
    val s = scalarComponentOf(v1, v2)

    return s / v2.getDistance()
}


/**
 * Calculate the scale multiplier according to the fit scale
 *
 */
fun Math.computeScaleMultiplierWithFit(
    srcWidth: Int,
    srcHeight: Int,
    dstWidth: Int,
    dstHeight: Int,
    fitScale: Boolean
): Double {
    val widthPercent = dstWidth / srcWidth.toDouble()
    val heightPercent = dstHeight / srcHeight.toDouble()
    return if (fitScale) {
        min(widthPercent, heightPercent)
    } else {
        max(widthPercent, heightPercent)
    }
}

/**
 * Calculate the scale multiplier according to the one side scale
 *
 */
fun Math.computeScaleMultiplierWithOneSide(sourceSize: Size, targetSize: Size): Float {
    if (!targetSize.isEmpty()) {
        val widthScaleFactor = targetSize.width.toFloat() / sourceSize.width
        val heightScaleFactor = targetSize.height.toFloat() / sourceSize.height
        val scaleFactor = min(widthScaleFactor, heightScaleFactor)
        return scaleFactor
    }
    if (targetSize.width > 0) {
        val scaleFactor = targetSize.width.toFloat() / sourceSize.width
        return scaleFactor
    }
    if (targetSize.height > 0) {
        val scaleFactor = targetSize.height.toFloat() / sourceSize.height
        return scaleFactor
    }
    return 1f
}


/**
 * Calculate the bounds of the Drawable to inside the container.
 *
 */
fun Math.calculateInsideBounds(contentSize: Size, containerBounds: Rect): Rect {
    val containerWidth = containerBounds.width
    val containerHeight = containerBounds.height
    if (contentSize.width <= containerWidth && contentSize.height <= containerHeight) {
        // center
        val left = containerBounds.left + (containerWidth - contentSize.width) / 2
        val top = containerBounds.top + (containerHeight - contentSize.height) / 2
        val right = left + contentSize.width
        val bottom = top + contentSize.height
        return Rect(left, top, right, bottom)
    } else {
        // fit
        val widthScale = containerWidth.toFloat() / contentSize.width
        val heightScale = containerHeight.toFloat() / contentSize.height
        val scale = min(widthScale, heightScale)
        val scaledWidth = (contentSize.width * scale).toInt()
        val scaledHeight = (contentSize.height * scale).toInt()
        val left = containerBounds.left + (containerWidth - scaledWidth) / 2
        val top = containerBounds.top + (containerHeight - scaledHeight) / 2
        val right = left + scaledWidth
        val bottom = top + scaledHeight
        return Rect(left, top, right, bottom)
    }
}

/**
 * Calculate the bounds of the Drawable to crop the container.
 *
 */
fun Math.calculateCropBounds(contentSize: Size, containerBounds: Rect): Rect {
    val containerWidth = containerBounds.width
    val containerHeight = containerBounds.height
    val widthScale = containerWidth.toFloat() / contentSize.width
    val heightScale = containerHeight.toFloat() / contentSize.height
    val scale = max(widthScale, heightScale)
    val scaledWidth = (contentSize.width * scale).toInt()
    val scaledHeight = (contentSize.height * scale).toInt()
    val left = containerBounds.left + (containerWidth - scaledWidth) / 2
    val top = containerBounds.top + (containerHeight - scaledHeight) / 2
    val right = left + scaledWidth
    val bottom = top + scaledHeight
    return Rect(left, top, right, bottom)
}
